/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.rcs.servlets.utils;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.StaxDriver;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.io.Writer;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author ruben
 */
public class FileUtils {

  private static final int DEFAULT_BUFFER_SIZE = 1024 * 64; // ..bytes = 64KB.

  ////////////////////////////

  public static void copyFile(File in, File out) throws IOException {
    BufferedInputStream inf = new BufferedInputStream(new FileInputStream(in));
    BufferedOutputStream outf = new BufferedOutputStream(new FileOutputStream(out));

    try {
      copyStream(inf, outf);
    } finally {
      inf.close();
      outf.close();
      out.setLastModified(in.lastModified());
    }
  }

  public static void moveFile(File in, File out) throws IOException {
    copyFile(in, out);
    in.delete();
  }

  public static void copyFileToStream(File in, OutputStream out) throws IOException {
    copyFileToStream(in, out, 0);
  }

  public static void copyFileToStream(File inFile, OutputStream out, long start) throws IOException {
    RandomAccessFile in = new RandomAccessFile(inFile, "r");

    try {
      copyStream(in, out, start);
    } finally {
      in.close();
    }
  }

  public static void copyFileToStream(File inFile, OutputStream out, long start, long length) throws IOException {
    RandomAccessFile in = new RandomAccessFile(inFile, "r");

    try {
      copyStream(in, out, start, length);
    } finally {
      in.close();
    }
  }

  //////////////////////////
  public static void copyStream(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
    int size = in.read(buffer);

    while (size > 0) {
      out.write(buffer, 0, size);
      size = in.read(buffer);
    }
  }

  private static int readMax(InputStream in, byte[] buffer, long max) throws IOException {
    long l = Math.min(buffer.length, max);

    return in.read(buffer, 0, (int) l);
  }

  public static void copyStream(InputStream in, OutputStream out, long maxLength) throws IOException {
    byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
    int size = readMax(in, buffer, maxLength);

    while (size > 0 && maxLength > 0) {
      out.write(buffer, 0, size);

      maxLength -= size;
      size = readMax(in, buffer, maxLength);
    }
  }

  ///////////////////////////////////////////////////
  public static void copyStream(RandomAccessFile in, OutputStream out) throws IOException {
    copyStream(in, out, 0);
  }

  public static void copyStream(RandomAccessFile in, OutputStream out, long start) throws IOException {
    copyStream(in, out, start, in.length() - start + 1);
  }

  /**
   * Copy the given byte range of the given input to the given output.
   *
   * @param input The input to copy the given range to the given output for.
   * @param output The output to copy the given range from the given input for.
   * @param start Start of the byte range.
   * @param length Length of the byte range.
   * @throws IOException If something fails at I/O level.
   */
  public static void copyStream(RandomAccessFile in, OutputStream out, long start, long length) throws IOException {
    byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
    int read;

    if (in.length() == length)
      // Write full range.
      while ((read = in.read(buffer)) > 0)
        out.write(buffer, 0, read);
    else {
      // Write partial range.
      in.seek(start);
      long toRead = length;

      while ((read = in.read(buffer)) > 0)
        if ((toRead -= read) > 0)
          out.write(buffer, 0, read);
        else {
          out.write(buffer, 0, (int) toRead + read);
          break;
        }
    }
  }

  ///////////////////////////////////////////////////
  public static void tryCreateDir(String dirName) {
    File dir = new File(dirName);

    if (!dir.exists())
      dir.mkdirs();

    System.out.format("Creado el directorio %s%n", dir.getAbsolutePath());
  }

  /**
   * Close the given resource.
   *
   * @param resource The resource to be closed.
   */
  public static void close(Closeable resource) {
    if (resource != null)
      try {
        resource.close();
      } catch (IOException ignore) {
        // Ignore IOException. If you want to handle this anyway, it might be useful to know
        // that this will generally only be thrown when the client aborted the request.
      }
  }

  ///////////////////////////////////////////////////
  /**
   * Le el contenido de stream como una cadena de texto codificada en utf-8
   *
   */
  public static String readAsString(InputStream in) throws IOException {
    return readAsString(in, "UTF-8");
  }

  public static String readAsString(InputStream in, String encoding) throws IOException {
    BufferedReader rd = new BufferedReader(new InputStreamReader(in, null == encoding ? "UTF-8" : encoding));
    StringBuilder sb = new StringBuilder();
    String line = rd.readLine();

    while (null != line) {
      sb.append(line).append('\n');
      line = rd.readLine();
    }

    return sb.toString();
  }

  /**
   * Lee desde la url dada un archivo y lo convierte a texto.
   *
   * @param url la url de donde leer el archivo
   * @param agent el agente a poner en la cabecera User-Agent, si es nulo no se pone ninguno.
   * @return
   */
  public static String readAsString(URL url, String agent) throws IOException {
    URLConnection cn = url.openConnection();

    if (null != agent)
      cn.setRequestProperty("User-Agent", agent);

    InputStream in = cn.getInputStream();

    try {
      return readAsString(in, cn.getContentEncoding());
    } finally {
      in.close();
    }
  }

  public static Date getFileDate(File file) {
    long date = file.lastModified();
    return new Date(date);
  }

  ///////////////////////////////////////////////////

  private static final XStream stream = new XStream(new StaxDriver() {

    @Override
    public HierarchicalStreamWriter createWriter(Writer out) {
      try {
        out.write(String.format("<?xml version=\"1.0\" encoding=\"UTF-8\"?>%n"));
      } catch (IOException ex) {
        Logger.getLogger(FileUtils.class.getName()).log(Level.SEVERE, null, ex);
      }

      return new PrettyPrintWriter(out) ;
    }
  });

  public static XStream getXStream() {
    return stream;
  }

  public static <T> XStream getXStream(Class<T> clazz) {
    stream.processAnnotations(clazz);
    return stream;
  }

  public static File getConfigDir() {
    return new File(OS.get().getUserDir(), ".SimpleMediaWebServer");
  }

  public static File getConfigFile(String fileName) {
    return new File(getConfigDir(), fileName);
  }

  public static OutputStreamWriter getConfigWriter(String fileName) throws IOException {
    return new OutputStreamWriter(new FileOutputStream(getConfigFile(fileName)), "UTF-8" );
  }

  ///////////////////////////////////////////////////

  public static File getDefaultVideoDir() {
    return OS.get().getDefaultVideoDir() ;
  }

  public static File getDefaultAudioDir() {
    return OS.get().getDefaultAudioDir() ;
  }

  public static File getDefaultPictureDir() {
    return OS.get().getDefaultPictureDir() ;
  }
}